A typical computer program may be composed of smaller programs known as procedures or subroutines. When a computer program is executed, a contiguous range of program address space is typically allocated for the call stack. The call stack consists of a set of stack frames, one for each procedure invocation, where each stack frame includes information about the parameters, local and temporary variables, and enough program state to resume execution when a procedure returns.
During runtime, a typical stack frame is created and pushed onto the call stack when a procedure is called, and then popped or removed from the call stack when the associated procedure exits. The call stack therefore, represents a structure of dynamically nested procedures that are active within the execution of the program at any given time.
When an improper event or error arises during execution of a program, an exception may be signaled from within the executing procedure. Exception handling often requires the ability to remove or “unwind” a number of frames from the call stack in order to transfer control to an exception handling procedure that may have been further down on the stack.
Stack unwinding in the presence of an exception involves the process of removing the stack frames from the call stack one at a time until a stack frame is encountered that represents a procedure that is willing to handle the exception. Unwinding typically starts with an initial context record, which describes the most recent activation of a procedure. Upon removing a stack frame from the call stack the exception runtime code reconstructs the runtime context for the previous procedure frames. If the procedure is willing to handle the exception, the unwinding stops and control is transferred to an exception handler for the procedure.
As the stack is unwound, it is necessary to recover the values of preserved registers that were saved by each procedure in order to reconstruct the previous frame's context. When a procedure's frame is removed from the call stack the preserved registers for its corresponding procedure must be reloaded with its saved values of local variables. The information about which preserved register is saved for a given procedure and where it is saved (e.g., memory or another register) is generated by the compiler as unwind data, stored in the binary text segment of the program itself, according to a particular programming convention. Unwind data, sometimes known as metadata, is a description of information related to a contiguous sequence of instructions of the program.
However, after source code for a computer program has been compiled, tools may be employed to insert code for profiling, or to reorder and optimize basic blocks of the code, or to otherwise instrument the code in a manner, that perturbs the binary code. When the binary code has been perturbed, the unwind data may no longer reflect the correct information necessary for the proper execution of the program during exceptions. The traditional approach for “fixing” the unwind data is to perform the modifications at the source code level, recompile and relink the computer programs. However, such an approach is a potentially expensive and lengthy process. Moreover, returning to the source code may not provide the flexibility required.